/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#include "M2AnimData.h"

namespace PQGameCore
{
	class Vector4_pack {
	public:
		short x,y,z,w;
		Vector4_pack(short x0,short y0,short z0,short w0) : x(x0), y(y0), z(z0), w(w0) {}
	};

	Ogre::Vector4 unpackVector4(Vector4_pack t)
	{
		return Ogre::Vector4(
				float(t.x < 0? t.x + 32768 : t.x - 32767)/ 32767.0f, 
				float(t.y < 0? t.y + 32768 : t.y - 32767)/ 32767.0f,
				float(t.z < 0? t.z + 32768 : t.z - 32767)/ 32767.0f,
				float(t.w < 0? t.w + 32768 : t.w - 32767)/ 32767.0f);
	}

	M2AnimData::M2AnimData(M2Model* m2Model,M2AnimBlock animblock)
		: _m2Model(m2Model)
	{
		_m2File=_m2Model->getM2File();
		_animFileList=_m2Model->getAnimFiles();

		for(int i=0;i<MAX_ANIMATED;i++){
			_times[i]=0;
			_nTimes[i]=0;
			_data[i]=0;
			_nData[i]=0;
		}

		assert(_m2Model);
		assert(_m2File);
		init(animblock);	
	}

	void M2AnimData::init(M2AnimBlock animblock)
	{
		_globalSeqs=_m2File->getGlobSeqPtr();
		_interpType=animblock.type;
		_seqIndex=animblock.seq;
		
		if (_seqIndex!=-1) {
			assert(_globalSeqs);
		}

		//init translate animation
		assert(animblock.nTimes==animblock.nKeys);

		// _times
		Ogre::uint animSize=_animFileList.size();
		assert(animblock.nTimes==animblock.nKeys);//??
		for(int i=0; i < animblock.nTimes; i++) {
			M2AnimBlockHeader* bh=_m2File->getAnimBlockHeader(animblock.timesOffset,i);
			_nTimes[i]=bh->nEntries;
			if(bh->nEntries>0){
				if(_animFileList[i]&&_animFileList[i]->getSize()>bh->entryOffset){	
					_times[i] = (unsigned int*)((char*)_animFileList[i]->getBuffer()+bh->entryOffset);
				}else if(_m2File->getSize()>bh->entryOffset){
					_times[i] = (unsigned int*)((char*)_m2File->getBuffer()+bh->entryOffset);
				}else{
					_times[i]=0;
				}

			}

		}

		// keyframes
		for(int i=0;i<animblock.nKeys;i++){
			M2AnimBlockHeader* bh=_m2File->getAnimBlockHeader(animblock.keysOffset,i);
			_nData[i]=bh->nEntries;
			if(bh->nEntries>0){
				if(_animFileList[i]&&_animFileList[i]->getSize()>bh->entryOffset){	
					_data[i] = (void*)((char*)_animFileList[i]->getBuffer()+bh->entryOffset);
				}else if(_m2File->getSize()>bh->entryOffset){
					_data[i] = (void*)((char*)_m2File->getBuffer()+bh->entryOffset);
				}else{
					_data[i]=0;
				}
				
			}
		}
	}
	Ogre::Vector3 M2AnimData::getVector3(Ogre::uint anim, Ogre::uint time)
	{
		if(_seqIndex>=0){
			assert("Not Implemented");
			if(_globalSeqs[_seqIndex]==0){
				time=0;
			}else{
				assert("Not Implemented");
			}
			anim=0;
		}
		

		if(_nData[anim]>1&&_nTimes[anim]>1&&_times[anim]!=0){
			int t1,t2,pos=0;
			
			int maxTime=_times[anim][_nTimes[anim]-1];
			if(maxTime>0){
				time%=maxTime;
			}

			//find time region
			for(int i=0;i<_nTimes[anim]-1;i++){
				if(time>=_times[anim][i]&&time<_times[anim][i+1]){
					pos=i;
					break;//
				}
			}

			t1=_times[anim][pos];
			t2=_times[anim][pos+1];

			float r=(float)(time-t1)/(float)(t2-t1);//difference
			

			switch(_interpType){
			case INTERPOLATION_LINEAR:{
				Ogre::Vector3 v1=*((Ogre::Vector3*)_data[anim]+pos);
				Ogre::Vector3 v2=*((Ogre::Vector3*)_data[anim]+pos+1);
				return (Ogre::Vector3)(v1*(1.0f - r) + v2*r);
			}
			case INTERPOLATION_HERMITE:{
				//For Hermite
				float h1 = 2.0f*r*r*r - 3.0f*r*r + 1.0f;
				float h2 = -2.0f*r*r*r + 3.0f*r*r;
				float h3 = r*r*r - 2.0f*r*r + r;
				float h4 = r*r*r - r*r;
				Ogre::Vector3 v1=*((Ogre::Vector3*)_data[anim]+pos*3);
				Ogre::Vector3 val_in=*((Ogre::Vector3*)_data[anim]+pos*3+1);
				Ogre::Vector3 val_out=*((Ogre::Vector3*)_data[anim]+pos*3+2);
				Ogre::Vector3 v2=*((Ogre::Vector3*)_data[anim]+pos*3+3);
				
				return (Ogre::Vector3)(v1*h1 + v2*h2 + val_in*h3 + val_out*h4);
			 }
			case INTERPOLATION_BEZIER:{
				//For Bezier
				float InverseFactor = (1.0f - r);
				float FactorTimesTwo = r*r;
				float InverseFactorTimesTwo = InverseFactor*InverseFactor;
				float h1 = InverseFactorTimesTwo * InverseFactor;
				float h2 = 3.0f * r * InverseFactorTimesTwo;
				float h3 = 3.0f * FactorTimesTwo * InverseFactor;
				float h4 = FactorTimesTwo * r;
				Ogre::Vector3 v1=*((Ogre::Vector3*)_data[anim]+pos*3);
				Ogre::Vector3 val_in=*((Ogre::Vector3*)_data[anim]+pos*3+1);
				Ogre::Vector3 val_out=*((Ogre::Vector3*)_data[anim]+pos*3+2);
				Ogre::Vector3 v2=*((Ogre::Vector3*)_data[anim]+pos*3+3);
				return (Ogre::Vector3)(v1*h1 + v2*h2 + val_in*h3 + val_out*h4);
			}
			case INTERPOLATION_NONE:
			default:{
				Ogre::Vector3 v1=*((Ogre::Vector3*)_data[anim]+pos);
				return v1;
				}
			}
		}else{
			// default value
			if (_nData[anim] == 0){
				return Ogre::Vector3();
			}else{
				Ogre::Vector3 v=*((Ogre::Vector3*)_data[anim]);
				return v;
			}

		}
	}

	Ogre::Vector4 M2AnimData::getUnpackVector4(Ogre::uint anim, Ogre::uint time)
	{
		if(_seqIndex>=0){
			assert("Not Implemented");
			if(_globalSeqs[_seqIndex]==0){
				time=0;
			}else{
				assert("Not Implemented");
			}
			anim=0;
		}

		if(_nData[anim]>1&&_nTimes[anim]>1){
			int t1,t2,pos=0;
			int maxTime=_times[anim][_nTimes[anim]-1];
			if(maxTime>0){
				time%=maxTime;
			}

			//find time region
			for(int i=0;i<_nTimes[anim]-1;i++){
				if(time>=_times[anim][i]&&time<_times[anim][i+1]){
					pos=i;
					break;//
				}
			}

			t1=_times[anim][pos];
			t2=_times[anim][pos+1];

			float r=(float)(time-t1)/(float)(t2-t1);//difference	

			switch(_interpType){
			case INTERPOLATION_LINEAR:{
				Vector4_pack v1_pack=*((Vector4_pack*)_data[anim]+pos);
				Vector4_pack v2_pack=*((Vector4_pack*)_data[anim]+pos+1);

				Ogre::Vector4 v1=unpackVector4(v1_pack);
				Ogre::Vector4 v2=unpackVector4(v2_pack);
				return (Ogre::Vector4)(v1*(1.0f - r) + v2*r);
			}
			case INTERPOLATION_HERMITE:{
				//For Hermite
				float h1 = 2.0f*r*r*r - 3.0f*r*r + 1.0f;
				float h2 = -2.0f*r*r*r + 3.0f*r*r;
				float h3 = r*r*r - 2.0f*r*r + r;
				float h4 = r*r*r - r*r;

				Vector4_pack v1_pack=*((Vector4_pack*)_data[anim]+pos*3);		
				Vector4_pack val_in_pack=*((Vector4_pack*)_data[anim]+pos*3+1);
				Vector4_pack val_out_pack=*((Vector4_pack*)_data[anim]+pos*3+2);
				Vector4_pack v2_pack=*((Vector4_pack*)_data[anim]+pos*3+3);

				Ogre::Vector4 v1=unpackVector4(v1_pack);
				Ogre::Vector4 v2=unpackVector4(v2_pack);
				Ogre::Vector4 val_in=unpackVector4(val_in_pack);
				Ogre::Vector4 val_out=unpackVector4(val_out_pack);

				return (Ogre::Vector4)(v1*h1 + v2*h2 + val_in*h3 + val_out*h4);
			 }
			case INTERPOLATION_BEZIER:{
				//For Bezier
				float InverseFactor = (1.0f - r);
				float FactorTimesTwo = r*r;
				float InverseFactorTimesTwo = InverseFactor*InverseFactor;
				float h1 = InverseFactorTimesTwo * InverseFactor;
				float h2 = 3.0f * r * InverseFactorTimesTwo;
				float h3 = 3.0f * FactorTimesTwo * InverseFactor;
				float h4 = FactorTimesTwo * r;
				
				Vector4_pack v1_pack=*((Vector4_pack*)_data[anim]+pos*3);		
				Vector4_pack val_in_pack=*((Vector4_pack*)_data[anim]+pos*3+1);
				Vector4_pack val_out_pack=*((Vector4_pack*)_data[anim]+pos*3+2);
				Vector4_pack v2_pack=*((Vector4_pack*)_data[anim]+pos*3+3);

				Ogre::Vector4 v1=unpackVector4(v1_pack);
				Ogre::Vector4 v2=unpackVector4(v2_pack);
				Ogre::Vector4 val_in=unpackVector4(val_in_pack);
				Ogre::Vector4 val_out=unpackVector4(val_out_pack);

				return (Ogre::Vector4)(v1*h1 + v2*h2 + val_in*h3 + val_out*h4);
				}
			case INTERPOLATION_NONE:
				{
				Vector4_pack v1_pack=*((Vector4_pack*)_data[anim]+pos*3);
				Ogre::Vector4 v1=unpackVector4(v1_pack);
				return v1;
				}
			default:{//shouldn't
				Vector4_pack v1_pack=*((Vector4_pack*)_data[anim]+pos*3);
				Ogre::Vector4 v1=unpackVector4(v1_pack);
				return v1;
				}
			}
		}else{
			// default value
			if (_nData[anim] == 0){
				return Ogre::Vector4(0,0,0,1);
			}else{
				Vector4_pack v_pack=*((Vector4_pack*)_data[anim]);
				Ogre::Vector4 v=unpackVector4(v_pack);
				return v;
			}
		}
	}




}
